#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <unistd.h>

#include <poll.h>
#include <fcntl.h>
#include <sys/time.h>
#include <getopt.h>
#include <pthread.h>

#include <libudev.h>
#include <libinput.h>
#include <libevdev/libevdev.h>

#include <X11/Xlib.h>
#include <X11/Xutil.h>

// 程序版本号
#define PROJECT_VERSION "0.1"
// 气泡窗口内边距（控制文本与边框的距离）
#define BUBBLE_PADDING 35
// 窗口最小宽度（避免文本过短时窗口过小）
#define MIN_BUBBLE_WIDTH 220
// 窗口最小高度
#define MIN_BUBBLE_HEIGHT 80
// 显示字体（10x20为较大字体，清晰醒目）
#define BUBBLE_FONT "10x20"
// 气泡相对于鼠标的X偏移量（向右偏移20像素）
#define MOUSE_OFFSET_X 20
// 气泡相对于鼠标的Y偏移量（向上偏移80像素，避免遮挡鼠标）
#define MOUSE_OFFSET_Y -80
// 单个按键名称的最大长度
#define MAX_KEY_NAME 256
// 按键序列显示延迟时间（单位：秒），超时后自动清空
#define KEY_DISPLAY_DELAY 3
// 定时器检查间隔（单位：微秒），10ms检查一次超时状态
#define TIMER_INTERVAL 10000
// 一次最多显示的按键序列长度（可自定义1-10，当前为3个）
#define MAX_DISPLAY_KEYS 3

// -------------------------- 全局变量声明 --------------------------
// X11显示对象（连接X服务器）
Display *dpy;
// X11根窗口（屏幕最底层窗口）
Window root;
// 气泡窗口对象（显示按键序列的窗口）
Window bubble_win;
// 绘图上下文（用于在窗口中绘制文本和图形）
GC gc;
// 当前屏幕索引
int scr;
// 屏幕宽度和高度（像素）
int scr_width, scr_height;
// 存储当前显示的按键序列文本（缓冲区大小 = 单个按键长度 × 最大序列数）
char current_key[MAX_KEY_NAME * MAX_DISPLAY_KEYS] = { 0 };
// 保护current_key的互斥锁（避免多线程并发访问冲突）
pthread_mutex_t key_mutex = PTHREAD_MUTEX_INITIALIZER;
// 全局字体对象（存储加载的字体信息）
XFontStruct *global_font = NULL;
// 拖拽状态标记（true = 正在拖拽窗口）
bool is_dragging = false;
// 拖拽时鼠标相对于窗口左上角的X偏移（避免拖拽时窗口跳动）
int drag_offset_x = 0;
// 拖拽时鼠标相对于窗口左上角的Y偏移
int drag_offset_y = 0;
// 手动位置锁定标记（true = 拖拽后锁定窗口位置，不再跟随鼠标）
bool manual_position_set = false;
// 手动拖拽后窗口的X坐标（锁定后使用此位置）
int manual_window_x = 0;
// 手动拖拽后窗口的Y坐标
int manual_window_y = 0;
// 定时器线程ID（处理按键超时清空）
pthread_t timer_thread;
// 定时器运行状态（控制线程退出）
bool timer_running = true;
// 保护超时相关变量的互斥锁
pthread_mutex_t timer_mutex = PTHREAD_MUTEX_INITIALIZER;
// 最后一次按键的时间戳（用于计算超时）
time_t last_key_time = 0;

// 按键序列存储数组（循环存储最近MAX_DISPLAY_KEYS个按键）
char key_sequence[MAX_DISPLAY_KEYS][MAX_KEY_NAME];
// 序列索引（标记下一个按键的存储位置）
int sequence_index = 0;
// 保护按键序列的互斥锁（多线程读写序列时防冲突）
pthread_mutex_t sequence_mutex = PTHREAD_MUTEX_INITIALIZER;

// 错误码枚举（程序退出时返回不同错误类型）
enum error_code {
	NO_ERROR,        // 无错误
	UDEV_FAILED,     // udev初始化失败
	LIBINPUT_FAILED, // libinput初始化失败
	SEAT_FAILED,     // 分配seat0失败（输入设备访问权限）
	PERMISSION_FAILED// 权限不足或线程创建失败
};

// libinput输入处理数据结构（传递udev和libinput对象给线程）
struct input_handler_data {
	struct udev *udev;         // udev对象（设备管理）
	struct libinput *libinput; // libinput对象（输入事件捕获）
};

// -------------------------- 函数声明与实现 --------------------------
/**
 * @brief  更新按键序列并拼接为显示文本
 * @param  new_key: 新捕获的按键名称（如"Ctrl+A"、"Mouse: Left"）
 * @note   1. 循环覆盖存储最近的MAX_DISPLAY_KEYS个按键
 *         2. 用空格分隔按键序列（可修改为" | "等其他分隔符）
 *         3. 线程安全：加锁保护序列和显示文本的读写
 */
void update_key_sequence(const char *new_key)
{
	// 加锁保护序列和显示文本（避免多线程同时修改）
	pthread_mutex_lock(&sequence_mutex);
	pthread_mutex_lock(&key_mutex);

	// 存储新按键到序列数组（循环覆盖最旧的按键）
	strncpy(key_sequence[sequence_index], new_key, MAX_KEY_NAME - 1);
	key_sequence[sequence_index][MAX_KEY_NAME - 1] = '\0'; // 确保字符串结束符
	// 更新索引（循环：0→1→2→0...，MAX_DISPLAY_KEYS=3时）
	sequence_index = (sequence_index + 1) % MAX_DISPLAY_KEYS;

	// 拼接序列文本（初始化显示缓冲区）
	current_key[0] = '\0';
	int valid_count = 0; // 有效按键数量（排除空值）
	// 遍历序列数组，按"最新→次新→最旧"顺序拼接
	for (int i = 0; i < MAX_DISPLAY_KEYS; i++) {
		// 计算当前遍历的索引（循环读取，确保顺序正确）
		int idx = (sequence_index + i) % MAX_DISPLAY_KEYS;
		// 跳过空按键（初始化时或序列未满时）
		if (key_sequence[idx][0] == '\0') continue;

		// 按键之间用空格分隔（可修改为" | "、" → "等其他分隔符）
		if (valid_count > 0)
			strncat(current_key, " ", sizeof(current_key) - strlen(current_key) - 1);
		// 拼接当前按键到显示文本
		strncat(current_key, key_sequence[idx], sizeof(current_key) - strlen(current_key) - 1);
		valid_count++;
	}

	// 解锁（释放资源，允许其他线程访问）
	pthread_mutex_unlock(&key_mutex);
	pthread_mutex_unlock(&sequence_mutex);
}

/**
 * @brief  清空按键序列和显示文本
 * @note   超时后调用，清空所有存储的按键和当前显示内容
 */
void clear_key_sequence()
{
	// 加锁保护，避免并发修改
	pthread_mutex_lock(&sequence_mutex);
	pthread_mutex_lock(&key_mutex);

	// 清空序列数组中所有按键
	for (int i = 0; i < MAX_DISPLAY_KEYS; i++)
		key_sequence[i][0] = '\0';
	// 重置序列索引
	sequence_index = 0;
	// 清空显示文本
	current_key[0] = '\0';

	// 解锁
	pthread_mutex_unlock(&key_mutex);
	pthread_mutex_unlock(&sequence_mutex);
}

/**
 * @brief  初始化X11气泡窗口
 * @note   1. 连接X服务器，创建顶层无边框窗口
 *         2. 加载字体和绘图上下文
 *         3. 设置窗口属性（无边框、置顶、事件监听）
 */
void init_bubble_window()
{
	// 连接X服务器（DISPLAY环境变量指定的服务器）
	dpy = XOpenDisplay(NULL);
	if (!dpy) {
		fprintf(stderr, "Failed to connect X server\n");
		exit(1);
	}

	// 获取当前屏幕信息
	scr = DefaultScreen(dpy);
	scr_width = DisplayWidth(dpy, scr);  // 屏幕宽度
	scr_height = DisplayHeight(dpy, scr); // 屏幕高度
	root = RootWindow(dpy, scr);         // 获取根窗口
	printf("[DEBUG] Screen size: %dx%d\n", scr_width, scr_height);

	// 设置窗口属性
	XSetWindowAttributes attr;
	attr.override_redirect = True; // 不受窗口管理器控制（无边框、置顶）
	attr.background_pixel = WhitePixel(dpy, scr); // 窗口背景色（白色）
	attr.border_pixel = BlackPixel(dpy, scr);   // 窗口边框色（黑色）
	// 监听的事件：暴露事件（窗口需要重绘）、鼠标按键、鼠标移动（拖拽用）
	attr.event_mask = ExposureMask | ButtonPressMask | ButtonReleaseMask |
			  PointerMotionMask;

	// 计算窗口初始位置（屏幕中心）
	manual_window_x = scr_width / 2 - MIN_BUBBLE_WIDTH / 2;
	manual_window_y = scr_height / 2 - MIN_BUBBLE_HEIGHT / 2;
	// 创建气泡窗口
	bubble_win = XCreateWindow(
		dpy, root,                  // 父窗口为根窗口
		manual_window_x, manual_window_y, // 初始位置
		MIN_BUBBLE_WIDTH, MIN_BUBBLE_HEIGHT, // 初始大小
		3,                          // 边框宽度
		CopyFromParent, InputOutput, // 窗口类型（输入输出型）
		CopyFromParent,
		// 启用的窗口属性（无边框、背景色、边框色、事件监听）
		CWOverrideRedirect | CWBackPixel |
		CWBorderPixel | CWEventMask,
		&attr);
	printf("[DEBUG] Window created: pos(%d,%d) size(%dx%d)\n",
	       manual_window_x, manual_window_y, MIN_BUBBLE_WIDTH,
	       MIN_BUBBLE_HEIGHT);

	// 创建绘图上下文（用于绘制文本和边框）
	gc = XCreateGC(dpy, bubble_win, 0, NULL);
	if (!gc) {
		fprintf(stderr, "Failed to create GC\n");
		exit(1);
	}

	// 加载字体（优先加载指定字体，失败则尝试默认字体）
	global_font = XLoadQueryFont(dpy, BUBBLE_FONT);
	if (!global_font) {
		fprintf(stderr,
			"Warning: Failed to load font '%s', try default...\n",
			BUBBLE_FONT);
		global_font = XLoadQueryFont(dpy, "9x15"); // 备选默认字体
	}
	if (!global_font) {
		fprintf(stderr, "Fatal: No font available\n");
		exit(1);
	}
	// 打印字体高度（调试用）
	printf("[DEBUG] Font loaded (height: % dpx)\n",
	       global_font->ascent + global_font->descent);
	// 设置绘图上下文的字体和前景色（黑色文本）
	XSetFont(dpy, gc, global_font->fid);
	XSetForeground(dpy, gc, BlackPixel(dpy, scr));

	// 显示窗口并强制刷新（确保窗口立即出现）
	XMapWindow(dpy, bubble_win);
	XFlush(dpy);
	printf("[DEBUG] Window displayed\n");
}

/**
 * @brief  处理窗口拖拽事件（鼠标左键拖拽）
 * @param  ev: X11事件对象（包含鼠标按键/移动信息）
 * @note   1. 左键按下：开始拖拽，记录鼠标偏移
 *         2. 左键释放：结束拖拽，锁定窗口位置
 *         3. 鼠标移动：拖拽中时更新窗口位置，边界检查
 */
void handle_drag_event(XEvent *ev)
{
	if (!ev) return; // 事件为空则返回

	switch (ev->type) {
	// 鼠标按键按下事件
	case ButtonPress:
		// 仅响应鼠标左键（Button1 = 左键）
		if (ev->xbutton.button == Button1) {
			is_dragging = true; // 标记为拖拽中
			// 记录鼠标相对于窗口左上角的偏移（避免拖拽时窗口跳动）
			drag_offset_x = ev->xbutton.x;
			drag_offset_y = ev->xbutton.y;
			printf("[DEBUG] Drag start: offset(%d,%d)\n",
			       drag_offset_x, drag_offset_y);
		}
		break;
	// 鼠标按键释放事件
	case ButtonRelease:
		if (ev->xbutton.button == Button1) {
			is_dragging = false; // 结束拖拽
			manual_position_set = true; // 锁定窗口位置
			// 获取当前窗口的实际位置（更新手动位置变量）
			Window dummy;
			unsigned int width, height, border, depth;
			XGetGeometry(dpy, bubble_win, &dummy, &manual_window_x,
				     &manual_window_y, &width, &height, &border,
				     &depth);
			printf("[DEBUG] Drag end: pos locked(%d,%d)\n",
			       manual_window_x, manual_window_y);
		}
		break;
	// 鼠标移动事件
	case MotionNotify:
		if (is_dragging) { // 仅在拖拽中时处理
			// 计算窗口新位置（鼠标绝对位置 - 偏移量）
			int new_x = ev->xmotion.x_root - drag_offset_x;
			int new_y = ev->xmotion.y_root - drag_offset_y;

			// 边界检查：窗口不能超出屏幕范围
			if (new_x < 0) new_x = 0;
			if (new_y < 0) new_y = 0;
			if (new_x + MIN_BUBBLE_WIDTH > scr_width)
				new_x = scr_width - MIN_BUBBLE_WIDTH;
			if (new_y + MIN_BUBBLE_HEIGHT > scr_height)
				new_y = scr_height - MIN_BUBBLE_HEIGHT;

			// 移动窗口并刷新显示
			XMoveWindow(dpy, bubble_win, new_x, new_y);
			XFlush(dpy);
			printf("[DEBUG] Dragging: pos(%d,%d)\n", new_x, new_y);
		}
		break;
	default:
		break;
	}
}

/**
 * @brief  更新气泡窗口显示（绘制按键序列、调整窗口大小和位置）
 * @note   1. 拖拽锁定后使用手动位置，否则跟随鼠标
 *         2. 响应式调整窗口大小（根据文本长度自动适配）
 *         3. 绘制背景、边框和居中的按键序列文本
 */
void update_bubble()
{
	pthread_mutex_lock(&key_mutex); // 加锁保护显示文本

	// 显示文本为空时，清空窗口
	if (current_key[0] == '\0') {
		XClearWindow(dpy, bubble_win);
		pthread_mutex_unlock(&key_mutex);
		return;
	}

	int bubble_x, bubble_y; // 窗口最终位置
	// 拖拽锁定后，使用手动设置的位置
	if (manual_position_set) {
		bubble_x = manual_window_x;
		bubble_y = manual_window_y;
		printf("[DEBUG] Window pos: manual(%d,%d)\n", bubble_x,
		       bubble_y);
	} else {
		// 未拖拽时，窗口跟随鼠标
		Window root_ret, child_ret;
		int mouse_x, mouse_y, win_x, win_y;
		unsigned int mask_ret;
		// 获取鼠标当前位置（失败则默认屏幕中心）
		if (XQueryPointer(dpy, root, &root_ret, &child_ret, &mouse_x,
				  &mouse_y, &win_x, &win_y,
				  &mask_ret) != True) {
			mouse_x = scr_width / 2;
			mouse_y = scr_height / 2;
		}
		// 计算窗口位置（鼠标位置 + 偏移量）
		bubble_x = mouse_x + MOUSE_OFFSET_X;
		bubble_y = mouse_y + MOUSE_OFFSET_Y;
		printf("[DEBUG] Window pos: follow mouse(%d,%d)→(%d,%d)\n",
		       mouse_x, mouse_y, bubble_x, bubble_y);
	}

	// 计算按键序列文本的宽度和高度（适配字体）
	int text_width = XTextWidth(global_font, current_key, strlen(current_key));
	int text_height = global_font->ascent + global_font->descent;
	printf("[DEBUG] Text size: %s (w:%d h:%d)\n", current_key, text_width,
	       text_height);

	// 响应式调整窗口大小（文本长度 + 内边距）
	int bubble_width = text_width + 2 * BUBBLE_PADDING;
	int bubble_height = text_height + 2 * BUBBLE_PADDING;
	// 限制窗口大小范围（最小不小于设定值，最大不超过屏幕1/2）
	if (bubble_width < MIN_BUBBLE_WIDTH)
		bubble_width = MIN_BUBBLE_WIDTH;
	if (bubble_height < MIN_BUBBLE_HEIGHT)
		bubble_height = MIN_BUBBLE_HEIGHT;
	if (bubble_width > scr_width / 2)
		bubble_width = scr_width / 2;
	if (bubble_height > scr_height / 4)
		bubble_height = scr_height / 4;

	// 窗口位置边界检查（避免超出屏幕）
	if (bubble_x < 0)
		bubble_x = 0;
	if (bubble_y < 0)
		bubble_y = 0;
	if (bubble_x + bubble_width > scr_width)
		bubble_x = scr_width - bubble_width;
	if (bubble_y + bubble_height > scr_height)
		bubble_y = scr_height - bubble_height;

	// 调整窗口位置和大小
	XMoveResizeWindow(dpy, bubble_win, bubble_x, bubble_y, bubble_width,
			  bubble_height);
	printf("[DEBUG] Window resized: size(%dx%d) final pos(%d,%d)\n",
	       bubble_width, bubble_height, bubble_x, bubble_y);

	// 绘制窗口内容：白色背景 → 黑色边框 → 居中文本
	XSetForeground(dpy, gc, WhitePixel(dpy, scr));
	XFillRectangle(dpy, bubble_win, gc, 0, 0, bubble_width, bubble_height); // 填充背景
	XSetForeground(dpy, gc, BlackPixel(dpy, scr));
	XDrawRectangle(dpy, bubble_win, gc, 0, 0, bubble_width - 1,
		       bubble_height - 1); // 绘制边框
	// 文本水平居中（窗口宽度 - 文本宽度）/ 2
	int text_x = (bubble_width - text_width) / 2;
	// 文本垂直居中（内边距 + 字体上行高度）
	int text_y = BUBBLE_PADDING + global_font->ascent;
	// 绘制按键序列文本
	XDrawString(dpy, bubble_win, gc, text_x, text_y, current_key,
		    strlen(current_key));
	XFlush(dpy); // 强制刷新，确保文本立即显示
	printf("[DEBUG] Bubble updated\n");

	pthread_mutex_unlock(&key_mutex); // 解锁
}

/**
 * @brief  输入处理线程（监听终端"stop"命令，用于退出程序）
 * @param  user_data: 传递的参数（包含udev和libinput对象）
 * @note   线程持续读取终端输入，收到"stop\n"则清理资源并退出
 */
static void *handle_input(void *user_data)
{
	struct input_handler_data *data = user_data;
	char line[MAX_KEY_NAME]; // 存储终端输入的命令
	// 循环读取终端输入
	while (fgets(line, MAX_KEY_NAME, stdin) != NULL) {
		// 收到"stop"命令（换行符需匹配）
		if (strcmp(line, "stop\n") == 0) {
			timer_running = false; // 停止定时器线程
			pthread_cancel(timer_thread); // 取消定时器线程
			pthread_join(timer_thread, NULL); // 等待线程退出

			// 清理资源
			if (global_font)
				XFreeFont(dpy, global_font); // 释放字体
			libinput_unref(data->libinput); // 释放libinput
			udev_unref(data->udev);         // 释放udev
			XFreeGC(dpy, gc);               // 释放绘图上下文
			XDestroyWindow(dpy, bubble_win); // 销毁窗口
			XCloseDisplay(dpy);             // 关闭X服务器连接
			printf("[DEBUG] Stop command received, exit\n");
			exit(EXIT_SUCCESS); // 退出程序
		}
	}
	return NULL;
}

/**
 * @brief  libinput设备打开回调（libinput访问输入设备时调用）
 * @param  path: 设备路径（如/dev/input/eventX）
 * @param  flags: 打开标志（如O_RDWR）
 * @param  user_data: 自定义参数（未使用）
 * @return 成功返回设备文件描述符，失败返回错误码
 */
static int open_restricted(const char *path, int flags, void *user_data)
{
	// 打开设备文件
	int fd = open(path, flags);
	if (fd < 0)
		fprintf(stderr, "Failed to open device %s: %s\n", path,
			strerror(errno)); // 打印打开失败原因
	else
		printf("[DEBUG] Device opened: %s (fd:%d)\n", path, fd); // 打印成功信息
	return fd < 0 ? -errno : fd; // 返回文件描述符或错误码
}

/**
 * @brief  libinput设备关闭回调（libinput释放设备时调用）
 * @param  fd: 设备文件描述符
 * @param  user_data: 自定义参数（未使用）
 */
static void close_restricted(int fd, void *user_data)
{
	printf("[DEBUG] Device closed: fd=%d\n", fd);
	close(fd); // 关闭设备文件描述符
}

// libinput接口结构体（传递打开/关闭设备的回调函数）
static const struct libinput_interface interface = {
	.open_restricted = open_restricted,
	.close_restricted = close_restricted,
};

// -------------------------- 组合键处理相关 --------------------------
// 修饰键按下状态标记（Ctrl/Shift/Alt/WIN）
static bool ctrl_pressed = false;   // Ctrl键按下状态
static bool shift_pressed = false; // Shift键按下状态
static bool alt_pressed = false;   // Alt键按下状态
static bool win_pressed = false;    // WIN键（Meta键）按下状态

/**
 * @brief  判断按键是否为修饰键（Ctrl/Shift/Alt/WIN）
 * @param  key_code: 按键的keycode（如29=LeftCtrl，42=LeftShift）
 * @return true = 修饰键，false = 普通键
 */
bool is_modifier_key(uint32_t key_code)
{
	switch (key_code) {
	case 29:  // LeftCtrl
	case 97:  // RightCtrl
	case 42:  // LeftShift
	case 54:  // RightShift
	case 56:  // LeftAlt
	case 100: // RightAlt
	case 125: // LeftWIN
	case 126: // RightWIN
		return true;
	default:
		return false;
	}
}

/**
 * @brief  根据keycode获取修饰键名称（如29→"Ctrl"）
 * @param  key_code: 修饰键的keycode
 * @return 修饰键名称字符串（如"Ctrl"、"Shift"）
 */
const char *get_modifier_name(uint32_t key_code)
{
	switch (key_code) {
	case 29: case 97:
		return "Ctrl";
	case 42: case 54:
		return "Shift";
	case 56: case 100:
		return "Alt";
	case 125: case 126:
		return "WIN";
	default:
		return "";
	}
}

/**
 * @brief  处理键盘按键事件（捕获按键、拼接组合键、更新序列）
 * @param  keyboard: libinput键盘事件对象
 * @note   1. 修饰键：更新按下状态，单独按下时添加到序列
 *         2. 普通键：拼接已按下的修饰键（如Ctrl+A），添加到序列
 *         3. 仅处理按键按下事件（忽略释放事件）
 */
static void handle_key_event(struct libinput_event_keyboard *keyboard)
{
	if (!keyboard) return; // 事件为空则返回

	// 获取按键的keycode和状态（按下/释放）
	uint32_t key_code = libinput_event_keyboard_get_key(keyboard);
	enum libinput_key_state state =
		libinput_event_keyboard_get_key_state(keyboard);

	// 处理修饰键（Ctrl/Shift/Alt/WIN）
	if (is_modifier_key(key_code)) {
		const char *mod_name = get_modifier_name(key_code);
		// 更新修饰键按下状态（按下=true，释放=false）
		if (strcmp(mod_name, "Ctrl") == 0)
			ctrl_pressed = (state == LIBINPUT_KEY_STATE_PRESSED);
		else if (strcmp(mod_name, "Shift") == 0)
			shift_pressed = (state == LIBINPUT_KEY_STATE_PRESSED);
		else if (strcmp(mod_name, "Alt") == 0)
			alt_pressed = (state == LIBINPUT_KEY_STATE_PRESSED);
		else if (strcmp(mod_name, "WIN") == 0)
			win_pressed = (state == LIBINPUT_KEY_STATE_PRESSED);

		// 修饰键单独按下时，添加到按键序列
		if (state == LIBINPUT_KEY_STATE_PRESSED) {
			char mod_key[MAX_KEY_NAME];
			snprintf(mod_key, sizeof(mod_key), "%s", mod_name);
			update_key_sequence(mod_key); // 添加到序列

			// 更新最后按键时间戳（重置超时）
			pthread_mutex_lock(&timer_mutex);
			last_key_time = time(NULL);
			pthread_mutex_unlock(&timer_mutex);

			update_bubble(); // 刷新窗口显示
			printf("[Key Captured] %s (code: %d)\n", mod_name, key_code);
			fflush(stdout); // 强制刷新终端输出
		}
		return;
	}

	// 仅处理普通键的按下事件（忽略释放事件）
	if (state != LIBINPUT_KEY_STATE_PRESSED)
		return;

	printf("[DEBUG] Key captured: code=%d\n", key_code);
	// 获取按键名称（如KEY_A→"A"，KEY_ENTER→"Enter"）
	const char *key_name = libevdev_event_code_get_name(EV_KEY, key_code);
	if (!key_name)
		key_name = "Unknown"; // 无法识别的按键

	// 普通键名称美化（替换为友好名称）
	const char *display_name = key_name;
	if (strcmp(key_name, "KEY_ENTER") == 0)
		display_name = "Enter";
	else if (strcmp(key_name, "KEY_BACKSPACE") == 0)
		display_name = "Backspace";
	else if (strcmp(key_name, "KEY_TAB") == 0)
		display_name = "Tab";
	else if (strcmp(key_name, "KEY_ESC") == 0)
		display_name = "Esc";
	else if (strcmp(key_name, "KEY_SPACE") == 0)
		display_name = "Space";
	else if (strcmp(key_name, "KEY_DELETE") == 0)
		display_name = "Del";
	else if (strcmp(key_name, "KEY_UP") == 0)
		display_name = "Up";
	else if (strcmp(key_name, "KEY_DOWN") == 0)
		display_name = "Down";
	else if (strcmp(key_name, "KEY_LEFT") == 0)
		display_name = "Left";
	else if (strcmp(key_name, "KEY_RIGHT") == 0)
		display_name = "Right";
	else if (strstr(key_name, "KEY_"))
		display_name = key_name + 4; // 去掉"KEY_"前缀（如KEY_S→"S"）

	// 拼接组合键（修饰键 + 普通键，如"Ctrl + A"）
	char final_key[MAX_KEY_NAME] = {0};
	if (ctrl_pressed) strcat(final_key, "Ctrl + ");
	if (shift_pressed) strcat(final_key, "Shift + ");
	if (alt_pressed) strcat(final_key, "Alt + ");
	if (win_pressed) strcat(final_key, "WIN + ");
	// 拼接普通键名称
	strncat(final_key, display_name, sizeof(final_key) - strlen(final_key) - 1);

	// 添加组合键到按键序列
	update_key_sequence(final_key);

	// 更新最后按键时间戳（重置超时）
	pthread_mutex_lock(&timer_mutex);
	last_key_time = time(NULL);
	pthread_mutex_unlock(&timer_mutex);

	update_bubble(); // 刷新窗口显示
	printf("[Key Captured] %s (code: %d)\n", final_key, key_code);
	fflush(stdout);
}

/**
 * @brief  处理鼠标按键事件（捕获鼠标左键/右键/中键）
 * @param  pointer: libinput鼠标事件对象
 * @note   仅处理鼠标按键按下事件，添加到按键序列（格式："Mouse: Left"）
 */
static void handle_button_event(struct libinput_event_pointer *pointer)
{
	if (!pointer) return; // 事件为空则返回

	// 获取鼠标按键的keycode和状态（按下/释放）
	uint32_t btn_code = libinput_event_pointer_get_button(pointer);
	enum libinput_button_state state =
		libinput_event_pointer_get_button_state(pointer);
	// 仅处理按下事件
	if (state != LIBINPUT_BUTTON_STATE_PRESSED)
		return;

	// 获取鼠标按键名称（如BTN_LEFT→"Left"）
	const char *btn_name = libevdev_event_code_get_name(EV_KEY, btn_code);
	if (!btn_name)
		btn_name = "Unknown";
	printf("[DEBUG] Mouse button captured: code=%d name=%s\n", btn_code,
	       btn_name);

	// 鼠标按键名称美化
	const char *display_name = btn_name;
	if (strcmp(btn_name, "BTN_LEFT") == 0)
		display_name = "Left";
	else if (strcmp(btn_name, "BTN_RIGHT") == 0)
		display_name = "Right";
	else if (strcmp(btn_name, "BTN_MIDDLE") == 0)
		display_name = "Middle";

	// 格式化鼠标按键名称（如"Mouse: Left"）
	char final_key[MAX_KEY_NAME];
	snprintf(final_key, sizeof(final_key), "Mouse: %s", display_name);

	// 添加到按键序列
	update_key_sequence(final_key);

	// 更新最后按键时间戳（重置超时）
	pthread_mutex_lock(&timer_mutex);
	last_key_time = time(NULL);
	pthread_mutex_unlock(&timer_mutex);

	update_bubble(); // 刷新窗口显示
	printf("[Mouse Captured] %s (code: %d)\n", final_key, btn_code);
	fflush(stdout);
}

/**
 * @brief  处理libinput输入事件（分发键盘/鼠标事件）
 * @param  libinput: libinput对象
 * @return 成功返回0，失败返回-1
 */
static int handle_events(struct libinput *libinput)
{
	if (!libinput) return -1; // libinput对象为空则返回失败

	struct libinput_event *event;
	// 分发libinput事件（从设备读取事件）
	if (libinput_dispatch(libinput) < 0)
		return -1;

	// 循环处理所有待处理事件
	while ((event = libinput_get_event(libinput)) != NULL) {
		printf("[DEBUG] libinput event received: type=%d\n",
		       libinput_event_get_type(event));
		// 根据事件类型分发到对应处理函数
		switch (libinput_event_get_type(event)) {
		case LIBINPUT_EVENT_KEYBOARD_KEY: // 键盘按键事件
			handle_key_event(
				libinput_event_get_keyboard_event(event));
			break;
		case LIBINPUT_EVENT_POINTER_BUTTON: // 鼠标按键事件
			handle_button_event(
				libinput_event_get_pointer_event(event));
			break;
		default: // 忽略其他未处理事件（如鼠标移动、滚轮等）
			break;
		}
		libinput_event_destroy(event); // 销毁事件对象（避免内存泄漏）
	}
	return 0;
}

/**
 * @brief  定时器线程（检查按键超时，超时后清空序列）
 * @param  arg: 自定义参数（未使用）
 * @note   每隔10ms检查一次，超过KEY_DISPLAY_DELAY秒无按键则清空显示
 */
static void *key_timer_thread(void *arg)
{
	(void)arg; // 忽略未使用的参数
	while (timer_running) { // 定时器运行状态
		usleep(TIMER_INTERVAL); // 休眠10ms

		pthread_mutex_lock(&timer_mutex); // 加锁保护超时变量
		// 检查是否超时（最后一次按键时间 + 延迟时间 ≤ 当前时间）
		if (last_key_time > 0 &&
		    (time(NULL) - last_key_time) >= KEY_DISPLAY_DELAY) {
			clear_key_sequence(); // 清空按键序列
			XClearWindow(dpy, bubble_win); // 清空窗口
			XFlush(dpy); // 强制刷新
			printf("[DEBUG] Key sequence timeout (%ds), cleared\n",
			       KEY_DISPLAY_DELAY);
			last_key_time = 0; // 重置时间戳
		}
		pthread_mutex_unlock(&timer_mutex); // 解锁
	}
	return NULL;
}

/**
 * @brief  主循环（监听输入事件和X11事件）
 * @param  libinput: libinput对象
 * @return 成功返回0，失败返回-1
 */
static int run_mainloop(struct libinput *libinput)
{
	if (!libinput) return -1; // libinput对象为空则返回失败

	// 创建定时器线程（处理超时清空）
	if (pthread_create(&timer_thread, NULL, key_timer_thread, NULL) != 0) {
		fprintf(stderr, "Timer thread create failed\n");
		return -1;
	}
	printf("[DEBUG] Timer thread started (timeout:%ds)\n",
	       KEY_DISPLAY_DELAY);

	// 配置poll监听libinput的文件描述符（等待输入事件）
	struct pollfd fd = { .fd = libinput_get_fd(libinput),
			     .events = POLLIN, // 监听可读事件
			     .revents = 0 };
	printf("[DEBUG] libinput fd monitored: %d\n", fd.fd);

	// 设置初始提示文本（告知用户使用方法）
	pthread_mutex_lock(&key_mutex);
	snprintf(current_key, sizeof(current_key), "Press any key (drag to move) | Max keys: %d", MAX_DISPLAY_KEYS);
	pthread_mutex_unlock(&key_mutex);
	update_bubble(); // 显示初始提示

	// 初始化事件处理（检查设备是否正常）
	if (handle_events(libinput) != 0) {
		fprintf(stderr, "Event init failed, check sudo\n");
		return -1;
	}

	// 主循环：持续监听输入事件和X11事件
	while (poll(&fd, 1, -1) > -1) {
		handle_events(libinput); // 处理libinput输入事件

		// 处理X11事件（如窗口暴露、拖拽等）
		while (XPending(dpy)) {
			XEvent ev;
			XNextEvent(dpy, &ev); // 获取X11事件
			printf("[DEBUG] X11 event received: type=%d\n",
			       ev.type);
			// 窗口暴露事件（需要重绘）
			if (ev.type == Expose &&
			    ev.xexpose.window == bubble_win) {
				update_bubble();
			} else {
				handle_drag_event(&ev); // 处理拖拽事件
			}
		}
	}
	return 0;
}

/**
 * @brief  打印帮助信息（--help选项）
 * @param  program_name: 程序名称（argv[0]）
 */
void print_help(char *program_name)
{
	printf("keybubble-libinput - Global Key Bubble Display\n");
	printf("Version " PROJECT_VERSION "\n");
	printf("Usage: %s [OPTIONS]\n", program_name);
	printf("Options:\n");
	printf("\t-h, --help\tShow help\n");
	printf("\t-v, --version\tShow version\n");
	printf("Features:\n");
	printf("\t1. Capture global keys/mouse buttons (sequence display)\n");
	printf("\t2. Draggable window (left click)\n");
	printf("\t3. Auto-resize window (responsive)\n");
	printf("\t4. Auto-hide after %ds\n", KEY_DISPLAY_DELAY);
	printf("\t5. Custom max display keys (define MAX_DISPLAY_KEYS)\n");
	printf("Note: Run with sudo\n");
}

/**
 * @brief  程序主函数（初始化、创建线程、启动主循环）
 * @param  argc: 参数个数
 * @param  argv: 参数数组
 * @return 退出码（NO_ERROR=0，其他为错误码）
 */
int main(int argc, char *argv[])
{
	// 命令行参数解析（--help和--version）
	const struct option long_options[] = { { "version", no_argument, 0,
						 'v' },
					       { "help", no_argument, 0, 'h' },
					       { NULL, 0, NULL, 0 } };

	int opt;
	while ((opt = getopt_long(argc, argv, "vh", long_options, NULL)) !=
	       -1) {
		switch (opt) {
		case 'v': // 显示版本号
			printf(PROJECT_VERSION "\n");
			return 0;
		case 'h': // 显示帮助信息
			print_help(argv[0]);
			return 0;
		default: // 无效选项
			fprintf(stderr, "Invalid option: -%c\n", opt);
			return 1;
		}
	}

	// 初始化按键序列（清空数组）
	clear_key_sequence();

	// 初始化X11气泡窗口
	init_bubble_window();

	// 初始化udev（设备管理库，用于枚举输入设备）
	struct udev *udev = udev_new();
	if (!udev) {
		fprintf(stderr, "udev init failed\n");
		return UDEV_FAILED;
	}
	printf("[DEBUG] udev init success\n");

	// 初始化libinput（输入事件捕获库）
	struct libinput *libinput =
		libinput_udev_create_context(&interface, NULL, udev);
	if (!libinput) {
		fprintf(stderr, "libinput init failed\n");
		udev_unref(udev);
		return LIBINPUT_FAILED;
	}
	printf("[DEBUG] libinput init success\n");

	// 分配seat0（输入设备访问权限，需要root）
	if (libinput_udev_assign_seat(libinput, "seat0") != 0) {
		fprintf(stderr, "seat assign failed\n");
		libinput_unref(libinput);
		udev_unref(udev);
		return SEAT_FAILED;
	}
	printf("[DEBUG] seat0 assigned\n");

	// 创建输入处理线程（监听stop命令）
	pthread_t input_thread;
	struct input_handler_data data = { udev, libinput };
	if (pthread_create(&input_thread, NULL, handle_input, &data) != 0) {
		fprintf(stderr, "Input thread create failed\n");
		libinput_unref(libinput);
		udev_unref(udev);
		return PERMISSION_FAILED;
	}
	printf("[DEBUG] Input thread created (listen stop)\n");

	// 打印启动信息
	printf("\nStarted! Press any key (sequence display, max %d keys), drag window with left click, enter 'stop' to exit\n",
	       MAX_DISPLAY_KEYS);
	// 启动主循环（监听事件）
	if (run_mainloop(libinput) < 0) {
		// 异常退出时清理资源
		timer_running = false;
		pthread_cancel(timer_thread);
		pthread_join(timer_thread, NULL);
		if (global_font)
			XFreeFont(dpy, global_font);
		libinput_unref(libinput);
		udev_unref(udev);
		return PERMISSION_FAILED;
	}

	// 正常退出时清理资源
	timer_running = false;
	pthread_cancel(timer_thread);
	pthread_join(timer_thread, NULL);
	if (global_font)
		XFreeFont(dpy, global_font);
	libinput_unref(libinput);
	udev_unref(udev);
	XFreeGC(dpy, gc);
	XDestroyWindow(dpy, bubble_win);
	XCloseDisplay(dpy);
	pthread_join(input_thread, NULL);

	return NO_ERROR;
}
